package com.kyin.satoken.filter;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.util.StreamUtils;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;

/*
 这个是一个自定义的HttpServletRequestWrapper，用于封装原始请求参数
 1. 封装原始请求参数
 2. 会把原来请求的参数一起传给后面的controller
 */
public class OriginRequestParamsRequestWrapper extends HttpServletRequestWrapper {
    private Map<String, String[]> extraParams;
    private byte[] body;
    private boolean isJsonRequest;
    private ObjectMapper objectMapper = new ObjectMapper();
    private JSONObject jsonBody;

    public OriginRequestParamsRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        this.extraParams = new HashMap<>();
        if ("POST".equalsIgnoreCase(request.getMethod())) {
            cacheRequestBody(request);
            // 3. 解析表单请求体参数
            if (isFormContentType(request)) {
                parseFormBody();
            }else if (isJsonContentType(request)){ // 4. 解析json请求体参数
                parseJsonBody();
            }
        }
    }


    @Override
    public ServletInputStream getInputStream() {
        return new ByteArrayServletInputStream(this.body);
    }

    @Override
    public BufferedReader getReader() {
        return new BufferedReader(new InputStreamReader(
                new ByteArrayInputStream(this.body),
                StandardCharsets.UTF_8));
    }

    static class ByteArrayServletInputStream extends ServletInputStream {
        private final ByteArrayInputStream buffer;

        public ByteArrayServletInputStream(byte[] contents) {
            this.buffer = new ByteArrayInputStream(contents);
        }

        @Override
        public int read() { return buffer.read(); }
        @Override
        public boolean isFinished() { return buffer.available() == 0; }
        @Override
        public boolean isReady() { return true; }
        @Override
        public void setReadListener(ReadListener listener) { }
    }


    @Override
    public String getParameter(String name) {
        String[] values = extraParams.get(name);
        if (values != null) {
            return values[0];
        }
        return super.getParameter(name);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        Map<String, String[]> map = new HashMap<>(super.getParameterMap());
        map.putAll(extraParams);
        return map;
    }

    @Override
    public Enumeration<String> getParameterNames() {
        Set<String> names = new HashSet<>();
        names.addAll(Collections.list(super.getParameterNames()));
        names.addAll(extraParams.keySet());
        return Collections.enumeration(names);
    }

    @Override
    public String[] getParameterValues(String name) {
        String[] values = extraParams.get(name);
        if (values != null) {
            return values;
        }
        return super.getParameterValues(name);
    }



    public void addParameter(String name, String... values) {
        extraParams.put(name, values);
        if (isJsonRequest && jsonBody != null) {
            jsonBody.put(name, values);
            updateBodyFromJson();
        }
    }

    public String getJsonBody() {
        return jsonBody != null ? jsonBody.toString() : null;
    }

    private void updateBodyFromJson() {
        try {
            this.body = objectMapper.writeValueAsBytes(jsonBody);
        } catch (Exception e) {
            throw new RuntimeException("JSON序列化失败", e);
        }
    }

    private void cacheRequestBody(HttpServletRequest request) throws IOException {
       this.body = StreamUtils.copyToByteArray(request.getInputStream());
    }

    private boolean isFormContentType(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType != null && (
                contentType.contains("x-www-form-urlencoded") ||
                        contentType.contains("multipart/form-data")
        );
    }

    private void parseFormBody() throws UnsupportedEncodingException {
        if (body == null || body.length == 0) return;

        String bodyStr = new String(body, getCharacterEncoding());
        String[] pairs = bodyStr.split("&");

        for (String pair : pairs) {
            String[] kv = pair.split("=", 2);
            if (kv.length == 2) {
                addParamValue(kv[0], kv[1]);
            }
        }
    }


    private void parseJsonBody() throws UnsupportedEncodingException {
        this.isJsonRequest = true;
        if (body == null || body.length == 0) return;
        String jsonBodyStr = new String(body, getCharacterEncoding());
        this.jsonBody = JSONUtil.parseObj(jsonBodyStr);
        jsonBody.forEach((k, v) -> addParamValue(k, String.valueOf(v)));
    }

    private boolean isJsonContentType(HttpServletRequest request) {
        String contentType = request.getContentType();
        return contentType != null && (
                contentType.contains("application/json")
        );
    }

    private void addParamValue(String name, String value) {
        String[] values = extraParams.get(name);
        if (values == null) {
            extraParams.put(name, new String[]{value});
        } else {
            String[] newValues = Arrays.copyOf(values, values.length + 1);
            newValues[values.length] = value;
            extraParams.put(name, newValues);
        }
    }

}
